# -*- coding: utf-8 -*-
# @Author : cm_Simple
# @Time : 2022/1/4
# @Remark: 水浸 DB 层
import logging
import time
from typing import (Dict,
                    Tuple)
from mxsoftpy import DB
from mxsoftpy.exception import (DataError,
                                DBError)
try:
    from opm_pyirm import GetStatusById
except ImportError:
    try:
        from bsmiddle import GetStatusById
    except ImportError:
        def GetStatusById(*args):
            return 1, 2

from db.public.IOTManager.iot_base import IOTBaseDB
from index import _
from model.common.IOTManager.iot_base import SRoomIOTMiddleModel
from utils.classes.custom_exception import VerifyError


class GetTableById(DB):
    """
    通过设备 ID 获取 table 数据类 封装
    """

    def __init__(self, open_param, base_open_param, plugin_open_param):
        super().__init__()
        # 设备 打开参数
        self.open_param = open_param
        # base/EntityTemplate 打开参数
        self.base_open_param = base_open_param
        # base/PluginData 打开参数
        self.plugin_open_param = plugin_open_param

    @staticmethod
    def merge_sub_dict(dict_open: dict, child_sub_key: str) -> dict:
        """
        用于合并打开数据库文件 sub_key
        :param dict_open: 打开参数
        :param child_sub_key: 子健名
        :return 新打开参数
        """
        sub_key = dict_open['sub_key'] + '\\%s' % child_sub_key if 'sub_key' in dict_open.keys() else child_sub_key
        return {**dict_open, **{'sub_key': sub_key}}

    @staticmethod
    def exchange_dict_key(data_list: list, key_list: list, k: str) -> list:
        """
        修改列表里字典的 key 并且删除无用 key
        XXX 暂时如此编写
        :param data_list: 原始数据
        :param key_list: 保留 key
        :param k: 新增字段
        :return 格式化数据
        """
        # replace_dict_keys = replace_dict.keys()
        for item in data_list:
            # 删除无用 key
            list(map(item.__delitem__, filter(lambda x: x not in key_list, list(item.keys()))))
            item['time'] = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(item.pop('_CREATETIME_')))
            item['code'] = k
            # 修改数据格式
            for item_key, item_value in item.items():
                # if item_key in replace_dict_keys:
                #     item[replace_dict[item_key]] = item.pop(item_key)
                if item_key == 'State':
                    # item[item_key] = 0 if item_value == 1 else 1 if item_value in [1, 2, 3, 4] else 2
                    if item_value in [0, 5]:
                        item[item_key] = '离线'
                    elif item_value == 1:
                        item[item_key] = '正常'
                    else:
                        item[item_key] = '报警'
        return data_list

    def get_table_data_by_id(self, sub_key: list, table_key: dict) -> Tuple[list, list]:
        """
        通过设备 ID 获取对应的 table 表的数据
        :param sub_key: 设备 ID 列表
        :param table_key: 查询参数， record_by_time 方法参数
        :return 数据集， type list
        """
        if not table_key:
            table_key['days'] = 100
        splice_name = '.SubMonitor'
        device_type = self.tree.open(**self.open_param).sub_items(key_list=sub_key, prop_list=['mxdevicetype'])
        # 结果数据
        res, head = [], []
        # 模糊查询后可能会有多个设备，此处循环操作
        for k, v in device_type.items():
            # 获取 table 表名
            v['table_name'] = \
                self.tree.open(**self.merge_sub_dict(self.open_param, k + '\\' + k + splice_name)).sub_keys()[0]
            # 水浸模板
            att_dict = self.tree.open(**self.merge_sub_dict(self.base_open_param,
                                                            v['mxdevicetype'] + '\\MonitorEx')).sub_items()
            # XXX 数据库限制 MonitorEx 子健下只有一个子健，为一类
            plugin_name = list(att_dict.values())[0]['mxpluginname']
            # 通过模板找到 table 表 字段含义
            key_name = self.tree.open(**self.merge_sub_dict(self.plugin_open_param,
                                                            plugin_name + '\\Output')).sub_items()
            # 转换成键值对
            key_name_dict = {key: values['mxlabel'] for key, values in key_name.items()}
            # XXX 不考虑 不同设备情况
            head = [{'key': key, 'name': values['mxlabel']} for key, values in key_name.items()]
            key_name_dict_keys = key_name_dict.keys()
            res_keys = list(key_name_dict_keys) + ['State', '_CREATETIME_']
            # XXX table 操作只支持 record_by_time， 所以此处查询 table 数据暂时这样做
            try:
                data = self.table.open(file='MXSE', table=v['table_name']).record_by_time(**table_key)

            except DBError as e:
                logging.error(e)
                data = []
            data = self.exchange_dict_key(data, res_keys, k)
            res.extend(data)
        # 此处前端要求要有所有字段说明
        head.extend(
            [{'key': 'State', 'name': '状态'}, {'key': 'time', 'name': '时间'}, {'key': 'code', 'name': '设备ID'}])
        return res, head


class WaterCheckDB(IOTBaseDB):
    """
    水浸设备实体 DB
    """
    device_type = "Water"

    def __init__(self):
        super().__init__()
        self.main_open_param = {'file': 'ccubase', 'main_key': 'MXSE'}
        # 设备 打开参数
        self.open_param = {**self.main_open_param, **{'sub_key': '1'}}
        self.sg_open_param = {**self.main_open_param, **{'sub_key': '1\\1.SG'}}
        self.sd_open_param = {**self.main_open_param, **{'sub_key': '1\\1.SD'}}
        self.ts_open_param = {**self.main_open_param, **{'sub_key': '1\\1.TS'}}
        self.base_open_param = {'file': 'base', 'main_key': 'EntityTemplate'}
        self.plugin_open_param = {'file': 'base', 'main_key': 'PluginData'}
        self.like_name = 'Water'

    def get_parent_name(self, sub_key):
        """
        获取位置名称
        :param sub_key: 设备名
        :return 位置名称
        """
        if 'SG' in sub_key:
            parent = self.tree.open(**self.sg_open_param).sub_items(key_list=[sub_key], prop_list=['mxparent',
                                                                                                   'mxlabel'])
            ts = self.tree.open(**self.ts_open_param).sub_items(key_list=[parent[sub_key]['mxparent']],
                                                                prop_list=['mxlabel'])
            return ts[parent['mxparent']]['mxlabel'] + '\\' + parent[sub_key]['mxlabel']
        return self.tree.open(**self.ts_open_param).sub_items(key_list=[sub_key],
                                                              prop_list=['mxlabel'])[sub_key]['mxlabel']

    def get_description(self) -> list:
        """
        获取告警信息
        :return table 表数据
        """
        # XXX table 数据库 查询方法支持度不高，暂时全取数据，应用层做过滤, 不能使用 pandas , 一直崩
        try:
            return self.table.open(file='MXSE', table='AlertEventLog', ).record_by_time(days=100)
        # XXX 不清楚如何获取到错误代码
        except DBError:
            return []

    @staticmethod
    def get_device_status(device_id, table_data):
        """
        获取设备状态
        :param device_id: 设备ID
        :param table_data: table 表数据
        """
        # XXX table 数据库 查询方法支持度不高，暂时全取数据，应用层做过滤
        if table_data:
            for i in table_data:
                if i['DeviceId'] == device_id.get("code"):
                    return i['Description']
                elif i['DeviceId'] == device_id:
                    return i['Description']
        return '-'

    def process_data(self, k, res, sub_key, water_status, sd_data, df):
        """
        处理数据，转变 response 数据格式，并根据类型返回不同的数据维度。 因语句块臃肿， 单拉出来
        :param k: 设备 ID
        :param res: 结果数据预定义格式
        :param sub_key: 符合过滤条件的水浸设备 sub_key
        :param water_status: 根据状态查询
        :param sd_data: 所有水浸设备 位置， 名称
        :param df: 告警信息
        :return 数据集， type list
        """
        status_code_dict = {0: [1], 1: [2, 3, 4], 2: [0, 5]}
        # 设备状态

        status_code = GetStatusById(k)[1]  # water 2022-1-7 18:54 设备需要增加MXSE
        # 目前暂时如此定义， 各个状态数量
        if status_code in [0, 5]:
            res['offline'] += 1
        elif status_code == 1:
            res['normal'] += 1
        else:
            res['warning'] += 1
        # 更新位置以及状态为中文
        if k in sub_key:
            # 根据是否过滤状态，不满足则删除数据
            if water_status is None or status_code in status_code_dict.get(int(water_status), []):
                v = sd_data[k]
                status_k = 0 if status_code == 1 else 1 if status_code in [2, 3, 4] else 2
                # 区分类型，根据类型决定返回数据维度
                name = v.get("mxlabel", "")
                device_type = v.get("mxdevicetype", "")
                v.clear()

                if self.like_name == 'TemperatureHumidity':
                    status_info = self.get_device_status(k, df)
                    gtb = GetTableById(self.sd_open_param, self.base_open_param, self.plugin_open_param)

                    table_data = gtb.get_table_data_by_id(sub_key, dict())
                    if table_data[0]:
                        table_data = table_data[0][0]
                        # 删除无用数据
                        # raise VerifyError(_("查询失败"))
                        [table_data.pop(del_key) for del_key in ['_CREATETIME_', 'time', 'State'] if
                         del_key in table_data.keys()]
                        v.update(table_data)
                    else:
                        v.update({"templature": "-", "humidity": "-"})
                    v.update({'alarm_info': status_info})

                # del v['mxdevicetype']
                # v.update({'position': self.get_parent_name(v.pop('mxparent')),
                v.update({'local': self.get_local(
                    SRoomIOTMiddleModel(device_type=self.like_name, iot_code=k, sroom_code="")), 'state': status_k,
                    'code': k, "name": name, "mxdevicetype": device_type})

            else:
                del sd_data[k]

    def read(self, **water_data) -> Dict[str, int]:
        """
        查看所有水浸设备，
        :return: 水浸设备
        """

        # 是否根据状态查询
        water_status = water_data.pop('state', None)
        # 找出符合过滤条件的水浸设备 sub_key
        # sub_key = self.tree.open(**self.sd_open_param).filter(**water_data)[-1]
        try:
            sub_key = self.tree.open(**self.sd_open_param).filter(mxdevicetype__like=r'*%s*' % self.like_name,
                                                                  mxlabel__like='*' + water_data.get(
                                                                      "name", "") + '*')[-1]
        except DBError as e:
            logging.error(e)
            raise VerifyError(_("查询失败"))
        if water_data.get("code") is not None:
            try:
                total, mid_keys = self.tree.open(self.main_key, "sroom_iot_middle",
                                                 file=self.file).filter(sroom_code=water_data["code"],
                                                                        device_type=self.device_type
                                                                        )
                mid_list = set()
                if mid_keys:
                    mid_data = self.tree.open(self.main_key, "sroom_iot_middle", file=self.file).sub_items(mid_keys)

                    for key, value in mid_data.items():
                        mid_list.add(value["iot_code"])
                sub_key = list(set(sub_key) & mid_list)
            except DBError as e:
                logging.error(e)
                raise VerifyError(_("查询错误"))

        sd_data = {}
        if sub_key:
            # 找出所有水浸设备 位置， 名称
            sd_data = self.tree.open(**self.sd_open_param).sub_items(key_list=sub_key,
                                                                     prop_list=['mxdevicetype', 'mxlabel', 'mxparent'])
        # 找出所有水浸设备 sub_key
        all_sub_key = self.tree.open(**self.sd_open_param).filter(mxdevicetype__like=r'*\\%s\\*' % self.like_name)
        # 结果数据格式预定义
        res = {'data': sd_data, 'total': all_sub_key[0], 'normal': 0, 'warning': 0, 'offline': 0}
        # 获取告警信息
        df = self.get_description()
        # 处理数据格式
        for k in all_sub_key[-1]:
            self.process_data(k, res, sub_key, water_status, sd_data, df)
        sd_data_list = [v for k, v in sd_data.items()]
        res['data'] = sd_data_list

        return res

    def device_info(self, device_list, state_dict):
        """
        处理数据
        """
        df = self.get_description()
        res_list = []
        if state_dict is None:
            state_dict = {"state": 1}
        if state_dict["state"] != 2:
            state_dict["state"] = 1

        for device in device_list:
            # 设备状态
            res_dict = {}

            status = GetStatusById(device["code"])[1]
            if state_dict is not None:
                if status != 1:
                    state_dict["state"] = 2
            status_k = 0 if status == 1 else 1 if status in [2, 3, 4] else 2
            # 区分类型，根据类型决定返回数据维度
            if self.like_name == 'TemperatureHumidity':
                status_info = self.get_device_status(device, df)
                gtb = GetTableById(self.sd_open_param, self.base_open_param, self.plugin_open_param)
                table_data = gtb.get_table_data_by_id(device["code"], dict())
                if table_data[0]:
                    table_data = table_data[0][0]
                    # 删除无用数据
                    # raise VerifyError(_("查询失败"))
                    [table_data.pop(del_key) for del_key in ['_CREATETIME_', 'time', 'State'] if
                     del_key in table_data.keys()]
                    res_dict.update(table_data)
                else:
                    res_dict.update({"templature": "-", "humidity": "-"})
                res_dict["alarm_info"] = status_info

            res_dict["state"] = status_k
            res_dict["name"] = device["mxlabel"]
            res_dict["local"] = self.get_local(
                SRoomIOTMiddleModel(device_type=self.like_name, iot_code=device["code"],
                                    sroom_code=""))
            res_dict["code"] = device["code"]
            res_list.append(res_dict)
        return res_list

    def param_middle(self, data_obj) -> dict:
        """
        基础查询 参数
        @param data_obj:
        @return:
        """
        return {"iot_code": data_obj.iot_code, "device_type": data_obj.device_type}


class WaterDevicesDB(DB):
    """
    水浸设备实体 DB
    """

    def __init__(self):
        super().__init__()
        self.main_open_param = {'file': 'ccubase', 'main_key': 'MXSE'}
        # 设备 打开参数
        self.open_param = {**self.main_open_param, **{'sub_key': '1\\1.SD'}}
        self.like_name = 'Water'

    def read(self) -> list:
        """
        查看所有水浸设备
        :return: 水浸设备
        """
        # 找出所有水浸设备 sub_key
        sub_key = self.tree.open(**self.open_param).filter(mxdevicetype__like=r'*\\%s\\*' % self.like_name)[-1]
        if not sub_key:
            raise DataError(_('不存在设备'))
        data = self.tree.open(**self.open_param).sub_items(key_list=sub_key, prop_list=['mxdevicetype', 'mxlabel'])
        res = []
        for k, v in data.items():
            status_code = GetStatusById(k)[1]
            status_k = 0 if status_code == 1 else 1 if status_code in [2, 3, 4] else 2
            v.update({'device_id': k, 'status': status_k})
            res.append(v)
        return res


class WaterDataDB(DB):
    """
    水浸设备数据实体 DB
    """

    def __init__(self):
        super().__init__()
        self.main_open_param = {'file': 'ccubase', 'main_key': 'MXSE'}
        # 设备 打开参数
        self.open_param = {**self.main_open_param, **{'sub_key': '1\\1.SD'}}
        self.base_open_param = {'file': 'base', 'main_key': 'EntityTemplate'}
        self.plugin_open_param = {'file': 'base', 'main_key': 'PluginData'}
        self.type_enum = {0: '全部', 1: '正常', 2: '报警'}

    def read(self, **water_data) -> Dict:
        """
        查看所有水浸设备
        :return: 水浸设备
        """
        water_keys = water_data.keys()
        if 'device_name' in water_keys:
            # 过滤水浸设备 sub_key
            sub_key = self.tree.open(**self.open_param).filter(mxlabel__like='*' + water_data['device_name'] + '*')[-1]
        else:
            sub_key = [water_data['device_id']]
        # 获取 table 表内的设备数据 类
        gtb = GetTableById(self.open_param, self.base_open_param, self.plugin_open_param)
        # 根据 contrast_type 来分走向
        if 'contrast_type' not in water_data.keys():
            # 获取 table 表内的设备数据
            res = gtb.get_table_data_by_id(sub_key, water_data['table_key'])
            res_data, res_head = res[0], res[1]
            # XXX 区分 状态类
            status_type = self.type_enum[water_data['status_type']]
            new_res = list(filter(lambda x: x['State'] == status_type, res_data)) \
                if water_data['status_type'] != 0 else res_data
            if 'pageindex' in water_keys and 'pagesize' in water_keys:
                pageindex = int(water_data['pageindex'])
                pagesize = int(water_data['pagesize'])
                device_data = new_res[(pageindex - 1) * pagesize:pageindex * pagesize]
            else:
                device_data = new_res
            return {'head': res_head, 'device_data': device_data, 'total': len(new_res)}
        else:
            # 获取 table 表内的设备数据 本期
            res = gtb.get_table_data_by_id(sub_key, water_data['key_contrast'])
            res_data, res_head = res[0], res[1]
            # 获取 table 表内的设备数据 上期
            res_last = gtb.get_table_data_by_id(sub_key, water_data['contrast_last'])
            res_data_last = res_last[0]
            return {'head': res_head,
                    'device_data': {'this': res_data, 'last': res_data_last}}
